package defectsim.resources;

import javax.vecmath.Vector2d;
import javax.vecmath.Vector3d;

import repast.simphony.parameter.Parameters;

import defectsim.SimulationEnvironment;
import defectsim.agents.DefectAgent;
import defectsim.agents.EdgeDislocation;
import defectsim.resources.deformation.Deformation;
import defectsim.resources.deformation.DeformationFactory;
import defectsim.resources.deformation.DeformationFactory.DeformationMode;


public class ExternalForce extends DefectResource implements Force {

	private SimulationEnvironment environment = SimulationEnvironment.getInstance();
	

	private Vector2d externalStress;
	private ForceType forceType;
	private Vector3d forceDirection;
	private int relaxationTickCount;
	private Deformation deformation;

	/*
	 *  External force class is singleton
	 */
	private static ExternalForce _instance = null;
	
	private ExternalForce(){
		forceType = ForceType.External;
	}

	static public ExternalForce getInstance() {
		if(_instance == null) {
			_instance = new ExternalForce();
		}
		return _instance;
	}
	
	public void initialize(){
		DeformationMode deformationMode = null;
		Parameters p = environment.getParameters();
		int mode = (Integer)p.getValue("deformationmode");
		switch(mode){
		case 0:
			deformationMode = DeformationMode.CyclicStress;
			break;
		case 1:
			deformationMode = DeformationMode.CyclicStrain;
			break;
		case 2:
			deformationMode = DeformationMode.MonotonicStress;
			break;
		case 3 :
			deformationMode = DeformationMode.ConstantStress;
			break;
		}
		DeformationFactory defFactory = new DeformationFactory();
		deformation = defFactory.createDeformation(deformationMode);
		String dir = (String)p.getValue("externalstressdirection");
		this.forceDirection = new Vector3d(Double.parseDouble(dir.substring(0,1)),Double.parseDouble(dir.substring(1,2)),
				Double.parseDouble(dir.substring(2,3)));
		this.relaxationTickCount = (Integer)p.getValue("relaxationtickcount");
		this.externalStress = new Vector2d(0.0, 0.0);
	}
	
	public Vector2d compute(DefectAgent da) {
		double b = 0.0;
		double schmidFactor = 0.0;
		if (da instanceof EdgeDislocation){
			EdgeDislocation dislocation = (EdgeDislocation)da;
			Vector3d slipPlane = dislocation.getSlipSystem().getPlane();
			Vector3d slipDirection = dislocation.getSlipSystem().getDirection();
			double cosPhi = slipPlane.dot(forceDirection) / (slipPlane.length() * forceDirection.length());
			double cosLambda = slipDirection.dot(forceDirection) / (slipDirection.length() * forceDirection.length());
			schmidFactor = cosPhi * cosLambda;
//			schmidFactor = Math.sqrt(6) / 9;
			b = dislocation.getUnitBurgersVector() * environment.getMaterial().getBurgersVector();
		}
		return new Vector2d(b * externalStress.x * schmidFactor, b * externalStress.y * schmidFactor);
	}

	public Vector2d getStress(DefectAgent da) {
		return externalStress;
	}
	
	public double getCycle(){
		return deformation.getCycle();
	}

	@Override
	public ForceType getForceType() {
		return forceType;
	}

	@Override
	public void update() {
		if (environment.getCurrentTick() > relaxationTickCount) {
			externalStress = deformation.updateExternalStress();
		}
	}
}
